using System;
using System.Linq;
using ChainSafe.Gaming.Evm.RLP;
using Nethereum.Hex.HexConvertors.Extensions;
using NUnit.Framework;

namespace ChainSafe.Gaming.Tests
{
    public class RLPTests
    {
        [Test]
        [TestCase("0xff", "0x81ff", TestName = "encode_singleHigh")]
        [TestCase("0x7e", "0x7e", TestName = "encode_singleLessMed")]
        [TestCase("0x02", "0x02", TestName = "encode_singleLow")]
        [TestCase("0x7f", "0x7f", TestName = "encode_singleMed")]
        [TestCase("0x80", "0x8180", TestName = "encode_singleMoreMed")]
        [TestCase("0x", "0x80", TestName = "encode_nullString")]
        [TestCase("0x00", "0x00", TestName = "encode_zeros_1")]
        [TestCase("0x0000", "0x820000", TestName = "encode_zeros_2")]
        [TestCase("0x000000", "0x83000000", TestName = "encode_zeros_3")]
        [TestCase("0x00000000", "0x8400000000", TestName = "encode_zeros_4")]
        [TestCase("0x00000000000000", "0x8700000000000000", TestName = "encode_zeros_7")]
        [TestCase("0x0000000000000000", "0x880000000000000000", TestName = "encode_zeros_8")]
        [TestCase("0x000000000000000000", "0x89000000000000000000", TestName = "encode_zeros_9")]
        [TestCase("0x000000000000000000000000000000", "0x8f000000000000000000000000000000", TestName = "encode_zeros_15")]
        [TestCase("0x00000000000000000000000000000000", "0x9000000000000000000000000000000000", TestName = "encode_zeros_16")]
        [TestCase("0x0000000000000000000000000000000000", "0x910000000000000000000000000000000000", TestName = "encode_zeros_17")]
        [TestCase("0x00000000000000000000000000000000000000000000000000000000000000", "0x9f00000000000000000000000000000000000000000000000000000000000000", TestName = "encode_zeros_31")]
        [TestCase("0x0000000000000000000000000000000000000000000000000000000000000000", "0xa00000000000000000000000000000000000000000000000000000000000000000", TestName = "encode_zeros_32")]
        [TestCase("0x000000000000000000000000000000000000000000000000000000000000000000", "0xa1000000000000000000000000000000000000000000000000000000000000000000", TestName = "encode_zeros_33")]
        [TestCase(
            "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
            "0xb50000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
            TestName = "encode_zeros_53")]
        [TestCase(
            "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
            "0xb6000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
            TestName = "encode_zeros_54")]
        [TestCase(
            "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
            "0xb700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
            TestName = "encode_zeros_55")]
        [TestCase(
            "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
            "0xb8380000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
            TestName = "encode_zeros_56")]
        [TestCase(
            "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
            "0xb839000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
            TestName = "encode_zeros_57")]
        [TestCase(
            "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
            "0xb83a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
            TestName = "encode_zeros_58")]
        [TestCase("0x01", "0x01", TestName = "encode_ones_1")]
        [TestCase("0x0101", "0x820101", TestName = "encode_ones_2")]
        [TestCase("0x010101", "0x83010101", TestName = "encode_ones_3")]
        [TestCase("0x01010101", "0x8401010101", TestName = "encode_ones_4")]
        [TestCase("0x01010101010101", "0x8701010101010101", TestName = "encode_ones_7")]
        [TestCase("0x0101010101010101", "0x880101010101010101", TestName = "encode_ones_8")]
        [TestCase("0x010101010101010101", "0x89010101010101010101", TestName = "encode_ones_9")]
        [TestCase("0x010101010101010101010101010101", "0x8f010101010101010101010101010101", TestName = "encode_ones_15")]
        [TestCase("0x01010101010101010101010101010101", "0x9001010101010101010101010101010101", TestName = "encode_ones_16")]
        [TestCase("0x0101010101010101010101010101010101", "0x910101010101010101010101010101010101", TestName = "encode_ones_17")]
        [TestCase("0x01010101010101010101010101010101010101010101010101010101010101", "0x9f01010101010101010101010101010101010101010101010101010101010101", TestName = "encode_ones_31")]
        [TestCase("0x0101010101010101010101010101010101010101010101010101010101010101", "0xa00101010101010101010101010101010101010101010101010101010101010101", TestName = "encode_ones_32")]
        [TestCase("0x010101010101010101010101010101010101010101010101010101010101010101", "0xa1010101010101010101010101010101010101010101010101010101010101010101", TestName = "encode_ones_33")]
        [TestCase(
            "0x0101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101",
            "0xb50101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101",
            TestName = "encode_ones_53")]
        [TestCase(
            "0x010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101",
            "0xb6010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101",
            TestName = "encode_ones_54")]
        [TestCase(
            "0x01010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101",
            "0xb701010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101",
            TestName = "encode_ones_55")]
        [TestCase(
            "0x0101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101",
            "0xb8380101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101",
            TestName = "encode_ones_56")]
        [TestCase(
            "0x010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101",
            "0xb839010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101",
            TestName = "encode_ones_57")]
        [TestCase(
            "0x01010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101",
            "0xb83a01010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101",
            TestName = "encode_ones_58")]
        [TestCase(
            "0x01010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101",
            "0xb86401010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101",
            TestName = "encode_ones_100")]
        [TestCase(
            "0x01010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101",
            "0xb903e801010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101",
            TestName = "encode_ones_1000")]
        public void EncodesRLPTest(string decoded, string encoded)
        {
            var byteArray = decoded.HexToByteArray();
            var expectedEncoded = encoded.HexToByteArray();

            var byteEncoded = RLP.EncodeElement(byteArray);

            Assert.AreEqual(expectedEncoded, byteEncoded);
        }

        [Test]
        [TestCase("0xff", "0x81ff", TestName = "decode_singleHigh")]
        [TestCase("0x7e", "0x7e", TestName = "decode_singleLessMed")]
        [TestCase("0x02", "0x02", TestName = "decode_singleLow")]
        [TestCase("0x7f", "0x7f", TestName = "decode_singleMed")]
        [TestCase("0x80", "0x8180", TestName = "decode_singleMoreMed")]
        [TestCase("0x00", "0x00", TestName = "decode_zeros_1")]
        [TestCase("0x0000", "0x820000", TestName = "decode_zeros_2")]
        [TestCase("0x000000", "0x83000000", TestName = "decode_zeros_3")]
        [TestCase("0x00000000", "0x8400000000", TestName = "decode_zeros_4")]
        [TestCase("0x00000000000000", "0x8700000000000000", TestName = "decode_zeros_7")]
        [TestCase("0x0000000000000000", "0x880000000000000000", TestName = "decode_zeros_8")]
        [TestCase("0x000000000000000000", "0x89000000000000000000", TestName = "decode_zeros_9")]
        [TestCase("0x000000000000000000000000000000", "0x8f000000000000000000000000000000", TestName = "decode_zeros_15")]
        [TestCase("0x00000000000000000000000000000000", "0x9000000000000000000000000000000000", TestName = "decode_zeros_16")]
        [TestCase("0x0000000000000000000000000000000000", "0x910000000000000000000000000000000000", TestName = "decode_zeros_17")]
        [TestCase("0x00000000000000000000000000000000000000000000000000000000000000", "0x9f00000000000000000000000000000000000000000000000000000000000000", TestName = "decode_zeros_31")]
        [TestCase("0x0000000000000000000000000000000000000000000000000000000000000000", "0xa00000000000000000000000000000000000000000000000000000000000000000", TestName = "decode_zeros_32")]
        [TestCase("0x000000000000000000000000000000000000000000000000000000000000000000", "0xa1000000000000000000000000000000000000000000000000000000000000000000", TestName = "decode_zeros_33")]
        [TestCase(
            "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
            "0xb50000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
            TestName = "decode_zeros_53")]
        [TestCase(
            "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
            "0xb6000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
            TestName = "decode_zeros_54")]
        [TestCase(
            "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
            "0xb700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
            TestName = "decode_zeros_55")]
        [TestCase(
            "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
            "0xb8380000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
            TestName = "decode_zeros_56")]
        [TestCase(
            "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
            "0xb839000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
            TestName = "decode_zeros_57")]
        [TestCase(
            "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
            "0xb83a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
            TestName = "decode_zeros_58")]
        [TestCase("0x01", "0x01", TestName = "decode_ones_1")]
        [TestCase("0x0101", "0x820101", TestName = "decode_ones_2")]
        [TestCase("0x010101", "0x83010101", TestName = "decode_ones_3")]
        [TestCase("0x01010101", "0x8401010101", TestName = "decode_ones_4")]
        [TestCase("0x01010101010101", "0x8701010101010101", TestName = "decode_ones_7")]
        [TestCase("0x0101010101010101", "0x880101010101010101", TestName = "decode_ones_8")]
        [TestCase("0x010101010101010101", "0x89010101010101010101", TestName = "decode_ones_9")]
        [TestCase("0x010101010101010101010101010101", "0x8f010101010101010101010101010101", TestName = "decode_ones_15")]
        [TestCase("0x01010101010101010101010101010101", "0x9001010101010101010101010101010101", TestName = "decode_ones_16")]
        [TestCase("0x0101010101010101010101010101010101", "0x910101010101010101010101010101010101", TestName = "decode_ones_17")]
        [TestCase("0x01010101010101010101010101010101010101010101010101010101010101", "0x9f01010101010101010101010101010101010101010101010101010101010101", TestName = "decode_ones_31")]
        [TestCase("0x0101010101010101010101010101010101010101010101010101010101010101", "0xa00101010101010101010101010101010101010101010101010101010101010101", TestName = "decode_ones_32")]
        [TestCase("0x010101010101010101010101010101010101010101010101010101010101010101", "0xa1010101010101010101010101010101010101010101010101010101010101010101", TestName = "decode_ones_33")]
        [TestCase(
            "0x0101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101",
            "0xb50101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101",
            TestName = "decode_ones_53")]
        [TestCase(
            "0x010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101",
            "0xb6010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101",
            TestName = "decode_ones_54")]
        [TestCase(
            "0x01010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101",
            "0xb701010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101",
            TestName = "decode_ones_55")]
        [TestCase(
            "0x0101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101",
            "0xb8380101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101",
            TestName = "decode_ones_56")]
        [TestCase(
            "0x010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101",
            "0xb839010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101",
            TestName = "decode_ones_57")]
        [TestCase(
            "0x01010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101",
            "0xb83a01010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101",
            TestName = "decode_ones_58")]
        [TestCase(
            "0x01010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101",
            "0xb86401010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101",
            TestName = "decode_ones_100")]
        [TestCase(
            "0x01010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101",
            "0xb903e801010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101",
            TestName = "decode_ones_1000")]
        public void DecodesRLPTest(string decoded, string encoded)
        {
            var byteArray = encoded.HexToByteArray();
            var expectedDecoded = decoded.HexToByteArray();

            var decodedIRLP = RLP.Decode(byteArray);
            Assert.AreEqual(expectedDecoded, decodedIRLP.RLPData);
        }

        [Test]
        public void DecodeShortDataTest()
        {
            var byteArray = "0x".HexToByteArray();

            var ex = Assert.Throws<ArgumentOutOfRangeException>(() =>
            {
                RLP.Decode(byteArray);
            });
            Assert.That(ex.Message.Contains("Index was out of range"));
        }

        [Test]
        public void DecodeShortSegmentDataTest()
        {
            var byteArray = "0xc8c382c3823145".HexToByteArray();

            var ex = Assert.Throws<Exception>(() =>
            {
                RLP.Decode(byteArray);
            });
            Assert.AreEqual("Invalid RLP c8c382c3823145", ex.Message);
        }

        [Test]
        public void EncodeMaxLengthTest()
        {
            var input = new byte[55];
            var expectedOutput = new byte[] { 0xB7 }.Concat(input).ToArray();

            var actualOutput = RLP.EncodeElement(input);

            Assert.AreEqual(expectedOutput, actualOutput);
        }
    }
}